package com.intellif.mozping.lock;

import org.I0Itec.zkclient.IZkDataListener;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;

import java.util.concurrent.CountDownLatch;

/**
 * @author by mozping
 * @Classname ZookeeperDistributeLock
 * @Description 基于异常的分布式锁
 * 实现思路是：
 * 1.客户端在zookeeper的指定路径下创建临时节点，创建成功，则获取到锁，创建失败，则认为没有获取到锁
 * 2.创建成功则进入业务逻辑处理，完成之后释放锁临时节点自动删除
 * 3.创建失败，则监听对应的路径，一旦锁的拥有者删除节点，则尝试去获取锁，获取不到则等待，在等待逻辑中就是继续监听
 * 可参考pic下面的流程图
 * @Date 2019/4/19 13:52
 */
public class ZookeeperDistributeLock extends ZookeeperAbstractLock {

    private static final Logger LOG = LoggerFactory.getLogger(ZookeeperDistributeLock.class);
    private CountDownLatch countDownLatch = null;

    @Override
    boolean tryLock() {
        try {
            //创建临时节点，创建成功则返回
            zkClient.createEphemeral(PATH);
            return true;
        } catch (Exception e) {
            //e.printStackTrace();
            //System.out.println("获取锁失败...");
            return false;
        }
    }

    @Override
    void waitLock() {
        IZkDataListener listener = new IZkDataListener() {
            public void handleDataChange(String dataPath, Object data) throws Exception {

            }

            public void handleDataDeleted(String dataPath) throws Exception {
                if (countDownLatch != null) {
                    countDownLatch.countDown();
                }
            }
        };

        //注册事件
        zkClient.subscribeDataChanges(PATH, listener);
        if (zkClient.exists(PATH)) {
            countDownLatch = new CountDownLatch(1);
            try {
                //如果起那么的节点一直存在，自己就等着，直到前面的节点被删除，方法再返回，
                // 这里的await就相当于一直阻塞在这里等着，删除的动作在listen里面实现
                countDownLatch.await();
            } catch (Exception e) {
                e.printStackTrace();
            }
        }
        //删除监听
        zkClient.unsubscribeDataChanges(PATH, listener);

    }

    public void unLock() {
        if (zkClient != null) {
            zkClient.delete(PATH);
            zkClient.close();
            LOG.info("释放锁资源...");
        }
    }
}
